{"cells":[{"attachments":{},"cell_type":"markdown","metadata":{},"source":["# Quantum detection of time series anomalies"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["First we read in all the config variables."]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[],"source":["import os\n","\n","\n","GLOBAL_SEED = os.getenv(\"GLOBAL_SEED\", 1989)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The list of packages required to run this tutorial is listed below."]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[{"name":"stdout","output_type":"stream","text":["covalent\n","matplotlib==3.7.0\n","pennylane  # No version is specified since we want to use the latest stable version\n","torch==1.13.1\n"]}],"source":["with open(\"./assets/requirements.txt\", \"r\") as file:\n","    for line in file:\n","        print(line.rstrip())"]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["# Install necessary packages\n","# !pip install -r ./requirements.txt"]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[],"source":["import covalent as ct\n","import torch"]},{"cell_type":"markdown","metadata":{},"source":["Seed Torch for reproducibility and set default tensor type"]},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[],"source":["torch.manual_seed(GLOBAL_SEED)\n","torch.set_default_tensor_type(torch.DoubleTensor)"]},{"cell_type":"code","execution_count":6,"metadata":{},"outputs":[],"source":["@ct.electron\n","def generate_normal_time_series_set(\n","    p: int, num_series: int, noise_amp: float, t_init: float, t_end: float, seed: int = GLOBAL_SEED\n",") -> tuple:\n","    \"\"\"Generate a normal time series data set where each of the p elements\n","    is drawn from a normal distribution x_t ~ N(0, noise_amp).\n","    \"\"\"\n","    torch.manual_seed(seed)\n","    X = torch.normal(0, noise_amp, (num_series, p))\n","    T = torch.linspace(t_init, t_end, p)\n","    return X, T"]},{"cell_type":"code","execution_count":7,"metadata":{},"outputs":[],"source":["@ct.electron\n","def generate_anomalous_time_series_set(\n","    p: int,\n","    num_series: int,\n","    noise_amp: float,\n","    spike_amp: float,\n","    max_duration: int,\n","    t_init: float,\n","    t_end: float,\n","    seed: int = GLOBAL_SEED,\n",") -> tuple:\n","    \"\"\"Generate an anomalous time series data set where the p elements of each sequence are\n","    from a normal distribution x_t ~ N(0, noise_amp). Then,\n","    anomalous spikes of random amplitudes and durations are inserted.\n","    \"\"\"\n","    torch.manual_seed(seed)\n","    Y = torch.normal(0, noise_amp, (num_series, p))\n","    for y in Y:\n","        # 5–10 spikes allowed\n","        spike_num = torch.randint(low=5, high=10, size=())\n","        durations = torch.randint(low=1, high=max_duration, size=(spike_num,))\n","        spike_start_idxs = torch.randperm(p - max_duration)[:spike_num]\n","        for start_idx, duration in zip(spike_start_idxs, durations):\n","            y[start_idx : start_idx + duration] += torch.normal(0.0, spike_amp, (duration,))\n","    T = torch.linspace(t_init, t_end, p)\n","    return Y, T"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Let's do a quick sanity check and plot a couple of these series. Despite the above function's ``@ct.electron`` decorators, these can still be used as normal Python functions without using the Covalent server. This is useful for quick checks like this:\n"]},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[],"source":["import matplotlib.pyplot as plt"]},{"cell_type":"code","execution_count":9,"metadata":{},"outputs":[],"source":["X_norm, T_norm = generate_normal_time_series_set(25, 25, 0.1, 0.1, 2 * torch.pi)\n","Y_anom, T_anom = generate_anomalous_time_series_set(25, 25, 0.1, 0.4, 5, 0, 2 * torch.pi)"]},{"cell_type":"code","execution_count":10,"metadata":{},"outputs":[{"data":{"image/png":"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","text/plain":["<Figure size 432x288 with 1 Axes>"]},"metadata":{"needs_background":"light"},"output_type":"display_data"}],"source":["plt.figure()\n","plt.plot(T_norm, X_norm[0], label=\"Normal\")\n","plt.plot(T_anom, Y_anom[1], label=\"Anomalous\")\n","plt.ylabel(\"$y(t)$\")\n","plt.xlabel(\"t\")\n","plt.grid()\n","leg = plt.legend()"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Taking a look at the above, the generated series are what we wanted. We have<br>\n","a simple human-parsable notion of what it is for a time series to be anomalous<br>\n","(big spikes). Of course, we don't need a complicated algorithm to be able to detect<br>\n","such anomalies but this is just a didactic example remember!<br>\n","<br>\n","Like many machine learning algorithms, training is done in mini-batches.<br>\n","Examining the form of the loss function<br>\n",":math:`\\mathcal{L}(\\boldsymbol{\\phi})`, we can see that time series are<br>\n","atomized. In other words, each term in the mean square error is for a given<br>\n",":math:`x_t` and not measured against the entire series :math:`x`. This<br>\n","allows us to break down the training set :math:`X` into<br>\n","time-series-independent chunks. Here’s an electron to do that:<br>\n"]},{"cell_type":"code","execution_count":11,"metadata":{},"outputs":[],"source":["@ct.electron\n","def make_atomized_training_set(X: torch.Tensor, T: torch.Tensor) -> list:\n","    \"\"\"Convert input time series data provided in a two-dimensional tensor format\n","    to atomized tuple chunks: (xt, t).\n","    \"\"\"\n","    X_flat = torch.flatten(X)\n","    T_flat = T.repeat(X.size()[0])\n","    atomized = [(xt, t) for xt, t in zip(X_flat, T_flat)]\n","    return atomized"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now wish to pass this to a cycled ``torch.utils.data.DataLoader``.<br>\n","However, this object is not<br>\n","`pickleable <https://docs.python.org/3/library/pickle.html#:~:text=%E2%80%9CPickling%E2%80%9D%20is%20the%20process%20whereby,back%20into%20an%20object%20hierarchy.>`__,<br>\n","which is a requirement of electrons in Covalent. We therefore use the<br>\n","below helper class to create a pickleable version.<br>\n"]},{"cell_type":"code","execution_count":12,"metadata":{},"outputs":[],"source":["from collections.abc import Iterator"]},{"cell_type":"code","execution_count":13,"metadata":{},"outputs":[],"source":["class DataGetter:\n","    \"\"\"A pickleable mock-up of a Python iterator on a torch.utils.Dataloader.\n","    Provide a dataset X and the resulting object O will allow you to use next(O).\n","    \"\"\"\n","    def __init__(self, X: torch.Tensor, batch_size: int, seed: int = GLOBAL_SEED) -> None:\n","        \"\"\"Calls the _init_data method on intialization of a DataGetter object.\"\"\"\n","        torch.manual_seed(seed)\n","        self.X = X\n","        self.batch_size = batch_size\n","        self.data = []\n","        self._init_data(\n","            iter(torch.utils.data.DataLoader(self.X, batch_size=self.batch_size, shuffle=True))\n","        )\n","    def _init_data(self, iterator: Iterator) -> None:\n","        \"\"\"Load all of the iterator into a list.\"\"\"\n","        x = next(iterator, None)\n","        while x is not None:\n","            self.data.append(x)\n","            x = next(iterator, None)\n","    def __next__(self) -> tuple:\n","        \"\"\"Analogous behaviour to the native Python next() but calling the\n","        .pop() of the data attribute.\n","        \"\"\"\n","        try:\n","            return self.data.pop()\n","        except IndexError:  # Caught when the data set runs out of elements\n","            self._init_data(\n","                iter(torch.utils.data.DataLoader(self.X, batch_size=self.batch_size, shuffle=True))\n","            )\n","            return self.data.pop()"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We call an instance of the above in an electron<br>\n"]},{"cell_type":"code","execution_count":14,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_training_cycler(Xtr: torch.Tensor, batch_size: int, seed: int = GLOBAL_SEED) -> DataGetter:\n","    \"\"\"Get an instance of the DataGetter class defined above, which behaves analogously to\n","    next(iterator) but is pickleable.\n","    \"\"\"\n","    return DataGetter(Xtr, batch_size, seed)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now have the means to create synthetic data and cycle through a<br>\n","training set. Next, we need to build our loss function<br>\n",":math:`\\mathcal{L}(\\boldsymbol{\\phi})` from electrons with the help of<br>\n","``PennyLane``.<br>\n"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Building the loss function<br>\n","--------------------------<br>\n","<br>\n","Core to building the loss function is the quantum circuit implementing<br>\n",":math:`V_t(\\boldsymbol{\\alpha}, \\boldsymbol{\\gamma}) := W^{\\dagger}(\\boldsymbol{\\alpha})D(\\boldsymbol{\\gamma}, t)W(\\boldsymbol{\\alpha})`.<br>\n","While there are existing templates in ``PennyLane`` for implementing<br>\n",":math:`W(\\boldsymbol{\\alpha})`, we use a custom circuit to implement<br>\n",":math:`D(\\boldsymbol{\\gamma}, t)`. Following the approach taken in<br>\n","[#Welch2014]_ (also explained in [#Baker2022]_ and the<br>\n","appendix of [#Cîrstoiu2020]_), we create the electron:<br>\n"]},{"cell_type":"code","execution_count":15,"metadata":{},"outputs":[],"source":["import pennylane as qml\n","from itertools import combinations"]},{"cell_type":"code","execution_count":16,"metadata":{},"outputs":[],"source":["@ct.electron\n","def D(gamma: torch.Tensor, n_qubits: int, k: int = None, get_probs: bool = False) -> None:\n","    \"\"\"Generates an n_qubit quantum circuit according to a k-local Walsh operator\n","    expansion. Here, k-local means that 1 <= k <= n of the n qubits can interact.\n","    See <https://doi.org/10.1088/1367-2630/16/3/033040> for more\n","    details. Optionally return probabilities of bit strings.\n","    \"\"\"\n","    if k is None:\n","        k = n_qubits\n","    cnt = 0\n","    for i in range(1, k + 1):\n","        for comb in combinations(range(n_qubits), i):\n","            if len(comb) == 1:\n","                qml.RZ(gamma[cnt], wires=[comb[0]])\n","                cnt += 1\n","            elif len(comb) > 1:\n","                cnots = [comb[i : i + 2] for i in range(len(comb) - 1)]\n","                for j in cnots:\n","                    qml.CNOT(wires=j)\n","                qml.RZ(gamma[cnt], wires=[comb[-1]])\n","                cnt += 1\n","                for j in cnots[::-1]:\n","                    qml.CNOT(wires=j)\n","    if get_probs:\n","        return qml.probs(wires=range(n_qubits))"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["While the above may seem a little complicated, since we only use a single<br>\n","qubit in this tutorial, the resulting circuit is merely a single :math:`R_z(\\theta)` gate."]},{"cell_type":"code","execution_count":17,"metadata":{},"outputs":[{"data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAS4AAACeCAYAAACM/eeCAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy88F64QAAAACXBIWXMAAAsTAAALEwEAmpwYAAATA0lEQVR4nO3deUwU5x8G8AdWgVX8UREB8VaoeN8XJkZr6x2ttWhFaLGtR6UKaalNrak0oqnaGqO0JQqtkapQtJp60WKtioqpRTQeaDg8oYIH3uxy7Pz+ME4YLhd2YeZln09CMjvM8c437MPsu+/M2EmSJIGISCD2ajeAiKi2GFxEJBwGFxEJh8FFRMJhcBGRcBhcRCQcBhcRCYfBRUTCYXARkXAYXEQkHAYXEQmHwUVEwmFwEZFwGFxEJBwGFxEJh8FFRMJhcBGRcBhcRCQcBhcRCYfBRUTCYXARkXAYXEQkHAYXEQmHwUVEwmFwEZFwGFxEJBwGFxEJh8FFRMJhcBGRcBhcRCQcBhcRCYfBRUTCYXARkXAYXEQkHAYXEQmHwUVEwmFwEZFwGFxEJBwGFxEJh8FFRMJhcBGRcBhcRCQcBhcRCYfBRUTCYXARkXAYXEQkHAYXEQmHwUVEwmFwEZFwmqjdANIWSZJw8eJFXLlyBZmZmcjOzsaDBw9gNBohSVK9779p06bQ6/Xo0KEDfHx84OPjg/79+8PZ2bne920O1kcbGFwEAEhPT8f27duRmJiI69evq90cBScnJ0ycOBEzZszAm2++CUdHxwZvA+ujMRLZtNzcXCkgIEACIMSPt7e3dPDgQdZHI/VRi50kNcD5LWnSrl27MGfOHDx+/Fgx38XFBX5+fvJHEQ8PDzg4OMDevn67RCVJQmlpKR49eoScnBxkZmbi3LlzyMjIqLTsrFmzsGXLFjg4ONRbe1gfDVM3N0ktv/76q6TT6RT/radNmybt379fMhqNajdP4dKlS9Ly5cslFxcXRXunTJlSb21lfbSNwWWDkpOTFW/Krl27SocOHVK7WS+Vn58vBQUFKd6cs2bNsvp+tFifgwcPSrdu3apxmYaqjxYwuGyMwWCQvL295T9sX19fKS8vT+1mmc1kMkmff/654s1pzT4drdXHZDJJK1eulABIzZs3l1avXi0VFxfXuHx91kcrGFw2JjIyUv6DdnFxkXJzc9VuUq2ZTCYpMDBQ0SFtMBissm0t1cdoNErBwcGKELK3t5cePXpU43r1WR+tYHDZkOLiYsnV1VX+g46KilK7SXWWn5+v6NOJj4+3eJtaqs/9+/el0aNHV/rWsE+fPmatXx/10RKOnLchhw8fxv379wEA7dq1w4IFC1RuUd25u7sjLCxMfp2YmGjxNrVSn5ycHPj5+eHvv/+u9Lvhw4ebtY36qI+WMLhsSPk/Xn9/f+h0OhVbY7mZM2fK0wcOHMCTJ08s2p4W6nPy5EkMHToUly9flufp9Xp5etiwYWZvy9r10RIGlw05ceKEPP3WW2+p2BLr6N69O3x9fQEARUVFSE9Pt2h7atcnISEBr732Gu7evQsAcHR0xI4dO9CsWTN5GXPPuADr10dLGFw2oqysDDk5OfLrvn37qtga6yl/HFlZWXXejpr1kSQJq1atwjvvvAOj0QgAcHNzw+HDhzFgwADcu3cPAODq6opXX321Vtu2Vn20hsFlI27cuIHi4mIAgIeHB1q0aKFyi6zDx8dHns7MzKzzdtSsT2hoKL788kvFvLt376J///5ITU2V5w0bNgx2dna12ra16qM1vMjaRty4cUOe7ty5s4otsa6uXbvK0+WPsbbUrM+SJUtQUFCAhIQExfxmzZrB1dVVfl2bj4kvWKs+WsMzLhtRVFQkT//vf/+zyjaDg4NhZ2cHOzs7NGnSBB06dMBHH32EwsJCAEBERIT8+6p+vv76a4vbUP7MqPwx1lZ91KcqJpMJN2/exOXLl3Hx4kVkZ2fD1dUV8fHx+Ouvvyot/+JbTqBuwWWt+mgNz7hsRElJiTzdtGlTq2339ddfR1xcHEpLS3Hp0iW8//77ePDgAXbs2IHw8PAqhxR88cUX2LNnDwICAizef/ljefFRry7qqz6lpaX4448/sG/fPpw7dw7nz5+v9O2evb09fHx8FGdHFdnZ2WHIkCG13r+16qM1DC4bVNt+kpo4OjrC09MTwPOxTzNnzsSWLVsAAM7OzpVucLdt2zbExcVh//79iv6XurLmsVhzm7du3UJUVBS2bt2K//77r8ZlTSYTrly5gitXrijmjxgxQv6ms1evXnXqd6uP+mgBg4usJicnB0lJSdWesaSlpWHu3Ln45ptvMG7cuAZuXcMoKSnBhg0bsHz5cjx9+rTKZVq1agU3NzfY29vj2bNnuHHjRpV3Tz1x4gSGDx+OO3fu1OljYmPG4CKLJCUlwdnZGWVlZTAYDACAdevWVVquoKAA06ZNw/Tp0xEeHt7QzWwQGRkZmDlzJs6fP6+Y7+HhgaCgIIwZMwZ9+/aFp6en4kzo0aNHcHFxqXKbqampcHJygpeXFyRJarRnULXF4CKLjBw5Eps2bUJRURE2b96M7OxsLF68WLFMSUkJ3n77bXh4eGDz5s0qtbR+HT16FFOnTsXDhw/leT179sTKlSsxceLEGvvNJk2apHgdHR2N06dPIzY2FgBgMBgQERGBe/fuYf369fV+w0IRsAJkkWbNmsHb2xu9e/fGhg0b8OzZM6xYsUKxzOLFi5GZmYndu3fDyclJpZbWn8OHD2PChAlyaOn1eqxZswbp6emYOnVqjaGVnZ2N48ePK+bNnz8fMTExOHnyJPr06SPP37hxI+bNm9cgD+XQOgYXWdXy5cuxevVq5OXlAQA2bdqEn376Cbt27UK7du1Ubp315ebmwt/fXx5q0KZNG5w4cQKfffaZWd9Oent7K14XFBTI08OHD8epU6cwY8YMeV5sbCy+//57K7VeXMIE1w8//IDOnTvDyckJAwcOREpKitpNoiqMGjUKPXr0QGRkJE6cOIFFixbhq6++QpcuXXD79m3FT/kxSiIqKytDYGCgfBxeXl44duwY+vfvb9b6MTExiteBgYFo3bq1Yp5er8f27dvx7rvvyvPCw8Nx7tw5C1svNiGCKyEhAaGhoVi6dCnS09Ph5+eHCRMmNKqRwI3Jp59+itjYWMTExKC4uBjLli1DmzZtKv2IfqF3VFQUjhw5AuD5WKwdO3ZUOoOqTmlpKebOnauYt3Xr1iqX1el02LRpE/r16wcAMBqNCAwMhMlkqnPbRSdEcK1btw7BwcGYO3cuunfvjo0bN6JNmzb48ccf1W6aTduyZQv27dtXaX5AQACMRiN+/vlnSM9vVlnlz4s3vYiKi4uxdu1a+fWyZcswcuRIs9cfPXq04vW+fftq/MbQ0dER8fHx8p0iLly4UGXtbYXmg6u4uBhpaWkYO3asYv7YsWNx8uRJlVpFti4hIQG5ubkAng93WLp0qdnrVtUhX/Gbxap069YNH3/8sfz622+/NXufjY3mg+vu3bsoKyuDh4eHYr6Hhwdu376tUqvI1kVHR8vTixYtqtXTo2vqkH+ZxYsXo0mT56OYUlJScPHiRbPXbUw4jquOwsLCcPbsWbWbYbYXN6drzE6ePIlRo0bVad3a1MdgMOD06dPy6w8//NDsdc3pkK9J27ZtMXnyZOzZswfA89H1PXv2NHv9xkLzZ1xubm7Q6XTIz89XzM/Pz5evkSNqSOnp6fJF2S+eZG2O2nTI12TEiBHy9KlTp2q9fmOg+TMuBwcHDBw4EMnJyfD395fnJycnY/r06aq1a/369artuy727t2LKVOmqN2MeuXn54e9e/fWad3a1OfMmTPydG3uAV/bDvnqDB06VJ5OS0ur9fqNgebPuADgk08+wZYtWxATE4OMjAyEhoYiLy9P6KfUaFlhYSE8PDyQnZ2tajv279+Pfv36ae5r/wcPHsjTbdu2NWudunbIV6X8QN7ylxjZEiGCa+bMmVi/fj0iIyPRr18/HD9+HAcOHEDHjh3VblqjtGrVKkycOFG+P1RoaCgGDRoEJycndOrUyaxtSJKEiIgIeHl5Qa/XY9SoUZU6kgsLCxEUFAQXFxe4uLggKChIEQqTJk2CTqfDtm3brHVoVrFw4UJkZGQgLS0NCxcuNGsdSzrkK/Ly8sI///yD8+fPCz2kxBJCBBfw/I/l2rVrMBqNSEtLq9WYGTLfs2fPEBMTgw8++ECeZzKZ8N577ylGb7/MmjVr8N1332Hjxo04ffo03N3d8cYbb+Dx48fyMgEBAThz5gySkpKQlJSEM2fOICgoSLGdOXPmYMOGDZYfmBW1bNkSvr6+GDBgANq3b//S5S3tkK/I0dERgwcPRq9evcz+R9LoqPMcWmpov//+u/xU48mTJ1e7XGJiotSyZUvJZDJV+t3atWuljh07vnRfJpNJ8vT0lCIjI+V5z549k5ydnaXo6GhJkiTp0qVLEgDp+PHj8jIpKSkSAOny5cvyvOvXr0sApMzMTIuO62WstZ2KSkpKKj2Nuqra1pf6Oi61CXPGRQ0jJSUFAwcOtOi+T1evXsXt27cVg4b1ej1GjhwpDxpOTU2Fs7Mz/Pz85GVGjBiB5s2bKwYWd+jQAR4eHjh69Gid26Mma3XIkxKDixSuX78OLy8vi7bxYmBwTYOGb9++jdatWyvexHZ2dnB3d680sNjLywvXrl2zqE1qsGaHPCkxuEihqKhIc/fM0uv1Qj6hxpod8qTE4CIFNzc3+fFidfViYHBNg4Y9PT1x584dxU3xJElCQUFBpYHF9+/ft6gzWw0V7/Q6e/Zs4Y5ByxhcpNC/f39cunTJom107twZnp6eSE5OlucZDAakpKTIfVrDhw/HkydPFE9qTk1NxdOnTxX9XgaDAdnZ2RgwYIBFbWpIpaWlmDdvnmJeXFycSq1pnBhcpDBu3DhkZGTg3r178rysrCycPXsWeXl5KC4uxtmzZ3H27Fn5OX25ubnw9fXF7t27ATzvqwoLC8Pq1avx22+/4cKFCwgODoazs7P8LMXu3btj/PjxmD9/PlJTU5Gamor58+dj8uTJ6Natm7zvU6dOwdHRUXGZi9ZV7JDfu3cvO+StTPOX/JB1lH/j1DQSvXfv3hgyZAji4+MREhIC4PlFxOW/1Xtxh8+rV6+iU6dOKCkpwZUrVxSjuJcsWYKioiKEhISgsLAQQ4cOxZ9//ql4NuD27duxaNEi+VFlU6ZMQVRUlKI9O3bswOzZs+X7UFVU/lgsCQdz6/My+fn5lTrkJ0+eXOftWcpa9dEaBpeNKH/bFaPRWOOyy5cvR2hoKBYsWACdTvfS0dmdOnWq9AAHOzs7REREICIiotr1WrZsiV9++aXa3xcUFGDnzp34999/q12m/LFY8qVCbepTk4oPClG7Q95a9dEaflS0Ea+88oo8XbHTvKLx48cjJCQEt27dqudW1ezatWvyswaqU/5Yqns2oTlqU5+arFixAiEhIbC3t8fWrVtV75C3Vn20hmdcNuLFdYfA8/FFJpOpxufzVXw2ohqGDBmCIUOG1LhMZmamPG3u/d6rUtv6VKdly5aIiopCSEgIfH1969wea7FWfbSGZ1w2wtXVFa6urgCej9V68fgw0WVlZcnTPj4+dd6OtevTvXt3TfQpWas+WsPgsiHlzwBEvYSmPKPRqLg8qPy3kXXB+oiDwWVDyl9ukpiYqGJLrOPQoUPyN5mdOnVCjx49LNoe6yMOBpcNKX8H2YMHD6re+W6p2NhYedrf39/ij2asjzgYXDbEx8cHgwYNAvD8sW/h4eEqt6juDh06JA94BYBZs2ZZvE3WRyAq31aHGtiRI0cU94aKi4tTu0m1lpeXJ3Xp0kU+htmzZ1tt26yPGBhcNiggIED+o7a3txfqzZmbmyv5+vrK7W/RooWUl5dn1X2wPtrH4LJBBQUFUs+ePRVnFjNmzJBu3rypdtOqVVJSIm3cuFFycXGR26zT6aSdO3dafV+sj/YxuGxUfn5+pTeng4ODNGXKFCkuLk66efOmVFZWpmobHz58KB07dkwKCwuT2rVrp2irTqeTEhMT623frI+22UlShYvMyGbcvXsXYWFh1T5FR6/Xo2vXrnB3d4eTk1OdRpLXhiRJKC4uxuPHj5GTk1PtdX7e3t6Ijo7GmDFj6rU9rI+GqZubpAVHjhyRBg8eXOmhDlr7adWqlRQZGSkZDAbWR0P1UQPPuEiWlZWFxMRE7N+/H5cvX1bck0sNDg4O6Nq1K0aMGIEZM2Zg1KhRaNq0qWrtYX20g8FF1SosLERWVhYePnwIg8FQ6dY19cHBwQF6vR4dOnRA+/btodPp6n2fdcX6qIfBRUTC4ch5IhIOg4uIhMPgIiLhMLiISDgMLiISDoOLiITD4CIi4TC4iEg4DC4iEg6Di4iEw+AiIuEwuIhIOAwuIhIOg4uIhMPgIiLhMLiISDgMLiISDoOLiITD4CIi4TC4iEg4DC4iEg6Di4iEw+AiIuEwuIhIOAwuIhIOg4uIhMPgIiLhMLiISDgMLiISDoOLiITD4CIi4TC4iEg4DC4iEg6Di4iEw+AiIuEwuIhIOAwuIhIOg4uIhMPgIiLhMLiISDgMLiISDoOLiITD4CIi4TC4iEg4DC4iEg6Di4iEw+AiIuEwuIhIOAwuIhLO/wEtATeMKHcLaAAAAABJRU5ErkJggg==","text/plain":["<Figure size 288x144 with 1 Axes>"]},"metadata":{},"output_type":"display_data"}],"source":["n_qubits = 1\n","dev = qml.device(\"default.qubit\", wires=n_qubits, shots=None)\n","D_one_qubit = qml.qnode(dev)(D)\n","_ = qml.draw_mpl(D_one_qubit, decimals=2)(torch.tensor([1, 0]), 1, 1, True)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["You may find the general function for :math:`D`` useful in case you want to experiment<br>\n","with more qubits and your own (possibly multi-dimensional) data after<br>\n","this tutorial.<br>\n","<br>\n","Next, we define a circuit to calculate the probability of certain bit strings being measured in the<br>\n","computational basis. In our simple example, we work only with one qubit<br>\n","and use the ``default.qubit`` local quantum circuit simulator.<br>\n"]},{"cell_type":"code","execution_count":18,"metadata":{},"outputs":[],"source":["@ct.electron\n","@qml.qnode(dev, interface=\"torch\", diff_method=\"backprop\")\n","def get_probs(\n","    xt: torch.Tensor,\n","    t: float,\n","    alpha: torch.Tensor,\n","    gamma: torch.Tensor,\n","    k: int,\n","    U: callable,\n","    W: callable,\n","    D: callable,\n","    n_qubits: int,\n",") -> torch.Tensor:\n","    \"\"\"Measure the probabilities for measuring each bitstring after applying a\n","    circuit of the form W†DWU to the |0⟩^(⊗n) state. This\n","    function is defined for individual sequence elements xt.\n","    \"\"\"\n","    U(xt, wires=range(n_qubits))\n","    W(alpha, wires=range(n_qubits))\n","    D(gamma * t, n_qubits, k)\n","    qml.adjoint(W)(alpha, wires=range(n_qubits))\n","    return qml.probs(range(n_qubits))"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["To take the projector<br>\n",":math:`|0\\rangle^{\\otimes n} \\langle 0 |^{\\otimes n}`, we consider only<br>\n","the probability of measuring the bit string of all zeroes, which is the<br>\n","0th element of the probabilities (bit strings are returned in<br>\n","lexicographic order).<br>\n"]},{"cell_type":"code","execution_count":19,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_callable_projector_func(\n","    k: int, U: callable, W: callable, D: callable, n_qubits: int, probs_func: callable\n",") -> callable:\n","    \"\"\"Using get_probs() above, take only the probability of measuring the\n","    bitstring of all zeroes (i.e, take the projector\n","    |0⟩^(⊗n)⟨0|^(⊗n)) on the time devolved state.\n","    \"\"\"\n","    callable_proj = lambda xt, t, alpha, gamma: probs_func(\n","        xt, t, alpha, gamma, k, U, W, D, n_qubits\n","    )[0]\n","    return callable_proj"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now have the necessary ingredients to build<br>\n",":math:`F(\\boldsymbol{\\phi}, x_t)`.<br>\n"]},{"cell_type":"code","execution_count":20,"metadata":{},"outputs":[],"source":["@ct.electron\n","def F(\n","    callable_proj: callable,\n","    xt: torch.Tensor,\n","    t: float,\n","    alpha: torch.Tensor,\n","    mu: torch.Tensor,\n","    sigma: torch.Tensor,\n","    gamma_length: int,\n","    n_samples: int,\n",") -> torch.Tensor:\n","    \"\"\"Take the classical expecation value of of the projector on zero sampling\n","    the parameters of D from normal distributions. The expecation value is estimated\n","    with an average over n_samples.\n","    \"\"\"\n","    # length of gamma should not exceed 2^n - 1\n","    gammas = sigma.abs() * torch.randn((n_samples, gamma_length)) + mu\n","    expectation = torch.empty(n_samples)\n","    for i, gamma in enumerate(gammas):\n","        expectation[i] = callable_proj(xt, t, alpha, gamma)\n","    return expectation.mean()"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now return to the matter of the penalty function :math:`P_{\\tau}`.<br>\n","We choose<br>\n","<br>\n",".. math::<br>\n","<br>\n","<br>\n","   P_{\\tau}(\\sigma) := \\frac{1}{\\pi} \\arctan(2 \\pi \\tau |\\sigma|).<br>\n","<br>\n","As an electron, we have"]},{"cell_type":"code","execution_count":21,"metadata":{},"outputs":[],"source":["@ct.electron\n","def callable_arctan_penalty(tau: float) -> callable:\n","    \"\"\"Create a callable arctan function with a single hyperparameter\n","    tau to penalize large entries of sigma.\n","    \"\"\"\n","    prefac = 1 / (torch.pi)\n","    callable_pen = lambda sigma: prefac * torch.arctan(2 * torch.pi * tau * sigma.abs()).mean()\n","    return callable_pen"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["The above is a sigmoidal function chosen because it comes with the useful property of being bounded.<br>\n","The prefactor of :math:`1/\\pi` is chosen such that the final loss<br>\n",":math:`\\mathcal{L}(\\boldsymbol{\\phi})` is defined in the range (0, 1),<br>\n","as defined in the below electron.<br>\n"]},{"cell_type":"code","execution_count":22,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_loss(\n","    callable_proj: callable,\n","    batch: torch.Tensor,\n","    alpha: torch.Tensor,\n","    mu: torch.Tensor,\n","    sigma: torch.Tensor,\n","    gamma_length: int,\n","    n_samples: int,\n","    callable_penalty: callable,\n",") -> torch.Tensor:\n","    \"\"\"Evaluate the loss function ℒ, defined in the background section\n","    for a certain set of parameters.\n","    \"\"\"\n","    X_batch, T_batch = batch\n","    loss = torch.empty(X_batch.size()[0])\n","    for i in range(X_batch.size()[0]):\n","        # unsqueeze required for tensor to have the correct dimension for PennyLane templates\n","        loss[i] = (\n","            1\n","            - F(\n","                callable_proj,\n","                X_batch[i].unsqueeze(0),\n","                T_batch[i].unsqueeze(0),\n","                alpha,\n","                mu,\n","                sigma,\n","                gamma_length,\n","                n_samples,\n","            )\n","        ).square()\n","    return 0.5 * loss.mean() + callable_penalty(sigma)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Training the normal model<br>\n","-------------------------<br>\n","<br>\n","Now equipped with a loss function, we need to minimize it with a<br>\n","classical optimization routine. To start this optimization, however, we<br>\n","need some initial parameters. We can generate them with the below<br>\n","electron.<br>\n"]},{"cell_type":"code","execution_count":23,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_initial_parameters(\n","    W: callable, W_layers: int, n_qubits: int, seed: int = GLOBAL_SEED\n",") -> dict:\n","    \"\"\"Randomly generate initial parameters. We need initial parameters for the\n","    variational circuit ansatz implementing W(alpha) and the standard deviation\n","    and mean (sigma and mu) for the normal distribution we sample gamma from.\n","    \"\"\"\n","    torch.manual_seed(seed)\n","    init_alpha = torch.rand(W.shape(W_layers, n_qubits))\n","    init_mu = torch.rand(1)\n","    # Best to start sigma small and expand if needed\n","    init_sigma = torch.rand(1)\n","    init_params = {\n","        \"alpha\": (2 * torch.pi * init_alpha).clone().detach().requires_grad_(True),\n","        \"mu\": (2 * torch.pi * init_mu).clone().detach().requires_grad_(True),\n","        \"sigma\": (0.1 * init_sigma + 0.05).clone().detach().requires_grad_(True),\n","    }\n","    return init_params"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Using the ``PyTorch`` interface to ``PennyLane``, we define our final<br>\n","electron before running the training workflow.<br>\n"]},{"cell_type":"code","execution_count":24,"metadata":{},"outputs":[],"source":["@ct.electron\n","def train_model_gradients(\n","    lr: float,\n","    init_params: dict,\n","    pytorch_optimizer: callable,\n","    cycler: DataGetter,\n","    n_samples: int,\n","    callable_penalty: callable,\n","    batch_iterations: int,\n","    callable_proj: callable,\n","    gamma_length: int,\n","    seed=GLOBAL_SEED,\n","    print_intermediate=False,\n",") -> dict:\n","    \"\"\"Train the QVR model (minimize the loss function) with respect to the\n","    variational parameters using gradient-based training. You need to pass a\n","    PyTorch optimizer and a learning rate (lr).\n","    \"\"\"\n","    torch.manual_seed(seed)\n","    opt = pytorch_optimizer(init_params.values(), lr=lr)\n","    alpha = init_params[\"alpha\"]\n","    mu = init_params[\"mu\"]\n","    sigma = init_params[\"sigma\"]\n","    def closure():\n","        opt.zero_grad()\n","        loss = get_loss(\n","            callable_proj, next(cycler), alpha, mu, sigma, gamma_length, n_samples, callable_penalty\n","        )\n","        loss.backward()\n","        return loss\n","    loss_history = []\n","    for i in range(batch_iterations):\n","        loss = opt.step(closure)\n","        loss_history.append(loss.item())\n","        if batch_iterations % 10 == 0 and print_intermediate:\n","            print(f\"Iteration number {i}\\n Current loss {loss.item()}\\n\")\n","    results_dict = {\n","        \"opt_params\": {\n","            \"alpha\": opt.param_groups[0][\"params\"][0],\n","            \"mu\": opt.param_groups[0][\"params\"][1],\n","            \"sigma\": opt.param_groups[0][\"params\"][2],\n","        },\n","        \"loss_history\": loss_history,\n","    }\n","    return results_dict"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Now, enter our first ``@ct.lattice``. This combines the above electrons,<br>\n","eventually returning the optimal parameters<br>\n",":math:`\\boldsymbol{\\phi}^{\\star}` and the loss with batch iterations.<br>\n"]},{"cell_type":"code","execution_count":25,"metadata":{},"outputs":[],"source":["@ct.lattice\n","def training_workflow(\n","    U: callable,\n","    W: callable,\n","    D: callable,\n","    n_qubits: int,\n","    k: int,\n","    probs_func: callable,\n","    W_layers: int,\n","    gamma_length: int,\n","    n_samples: int,\n","    p: int,\n","    num_series: int,\n","    noise_amp: float,\n","    t_init: float,\n","    t_end: float,\n","    batch_size: int,\n","    tau: float,\n","    pytorch_optimizer: callable,\n","    lr: float,\n","    batch_iterations: int,\n","):\n","    \"\"\"\n","    Combine all of the previously defined electrons to do an entire training workflow,\n","    including (1) generating synthetic data, (2) packaging it into training cyclers\n","    (3) preparing the quantum functions and (4) optimizing the loss function with\n","    gradient based optimization. You can find definitions for all of the arguments\n","    by looking at the electrons and text cells above.\n","    \"\"\"\n","    X, T = generate_normal_time_series_set(p, num_series, noise_amp, t_init, t_end)\n","    Xtr = make_atomized_training_set(X, T)\n","    cycler = get_training_cycler(Xtr, batch_size)\n","    init_params = get_initial_parameters(W, W_layers, n_qubits)\n","    callable_penalty = callable_arctan_penalty(tau)\n","    callable_proj = get_callable_projector_func(k, U, W, D, n_qubits, probs_func)\n","    results_dict = train_model_gradients(\n","        lr,\n","        init_params,\n","        pytorch_optimizer,\n","        cycler,\n","        n_samples,\n","        callable_penalty,\n","        batch_iterations,\n","        callable_proj,\n","        gamma_length,\n","        print_intermediate=False,\n","    )\n","    return results_dict"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Before running this workflow, we define all of the input parameters.<br>\n"]},{"cell_type":"code","execution_count":26,"metadata":{},"outputs":[],"source":["general_options = {\n","    \"U\": qml.AngleEmbedding,\n","    \"W\": qml.StronglyEntanglingLayers,\n","    \"D\": D,\n","    \"n_qubits\": 1,\n","    \"probs_func\": get_probs,\n","    \"gamma_length\": 1,\n","    \"n_samples\": 10,\n","    \"p\": 25,\n","    \"num_series\": 25,\n","    \"noise_amp\": 0.1,\n","    \"t_init\": 0.1,\n","    \"t_end\": 2 * torch.pi,\n","    \"k\": 1,\n","}"]},{"cell_type":"code","execution_count":27,"metadata":{},"outputs":[],"source":["training_options = {\n","    \"batch_size\": 10,\n","    \"tau\": 5,\n","    \"pytorch_optimizer\": torch.optim.Adam,\n","    \"lr\": 0.01,\n","    \"batch_iterations\": 100,\n","    \"W_layers\": 2,\n","}"]},{"cell_type":"code","execution_count":28,"metadata":{},"outputs":[],"source":["training_options.update(general_options)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We can now dispatch the lattice to the Covalent server.<br>\n"]},{"cell_type":"code","execution_count":29,"metadata":{},"outputs":[],"source":["tr_dispatch_id = ct.dispatch(training_workflow)(**training_options)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["If you are running the notebook version of this tutorial, if you<br>\n","navigate to http://localhost:48008/ you can view the workflow on the<br>\n","Covalent GUI. It will look like the screenshot below, showing nicely how all of the<br>\n","electrons defined above interact with each other in the workflow. You can<br>\n","also track the progress of the calculation here.<br>\n","<br>\n",".. figure:: ../demonstrations/univariate_qvr/covalent_tutorial_screenshot.png<br>\n","   :width: 85%<br>\n","   :align: center<br>\n","   :alt: Training workflow screenshot in Covalent<br>\n","<br>\n","<br>\n","   A screenshot of the Covalent GUI for the training workflow.<br>\n"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now pull the results back from the Covalent server:<br>\n"]},{"cell_type":"code","execution_count":30,"metadata":{},"outputs":[],"source":["ct_tr_results = ct.get_result(dispatch_id=tr_dispatch_id, wait=True)\n","results_dict = ct_tr_results.result\n","\n","# Check that the workflow executed successfully\n","assert str(ct_tr_results.status) == \"COMPLETED\""]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["and take a look at the training loss history:<br>\n"]},{"cell_type":"code","execution_count":32,"metadata":{},"outputs":[{"data":{"image/png":"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","text/plain":["<Figure size 432x288 with 1 Axes>"]},"metadata":{"needs_background":"light"},"output_type":"display_data"}],"source":["plt.figure()\n","plt.plot(results_dict[\"loss_history\"], \".-\")\n","plt.ylabel(\"Loss [$\\mathcal{L}$]\")\n","plt.xlabel(\"Batch iterations\")\n","plt.title(\"Loss function versus batch iterations in training\")\n","plt.grid()"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Tuning the threshold :math:`\\zeta`<br>\n","----------------------------------<br>\n","<br>\n","When we have access to labelled anomalous series (as we do in our toy<br>\n","problem here, often not the case in reality), we can tune the threshold<br>\n",":math:`\\zeta` to maximize some success metric. We choose to maximize the<br>\n","accuracy score as defined using the three electrons below.<br>\n"]},{"cell_type":"code","execution_count":33,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_preds_given_threshold(zeta: float, scores: torch.Tensor) -> torch.Tensor:\n","    \"\"\"For a given threshold, get the predicted labels (1 or -1), given the anomaly scores.\"\"\"\n","    return torch.tensor([-1 if score > zeta else 1 for score in scores])"]},{"cell_type":"code","execution_count":34,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_truth_labels(\n","    normal_series_set: torch.Tensor, anomalous_series_set: torch.Tensor\n",") -> torch.Tensor:\n","    \"\"\"Get a 1D tensor containing the truth values (1 or -1) for a given set of\n","    time series.\n","    \"\"\"\n","    norm = torch.ones(normal_series_set.size()[0])\n","    anom = -torch.ones(anomalous_series_set.size()[0])\n","    return torch.cat([norm, anom])"]},{"cell_type":"code","execution_count":35,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_accuracy_score(pred: torch.Tensor, truth: torch.Tensor) -> torch.Tensor:\n","    \"\"\"Given the predictions and truth values, return a number between 0 and 1\n","    indicating the accuracy of predictions.\n","    \"\"\"\n","    return torch.sum(pred == truth) / truth.size()[0]"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Then, knowing the anomaly scores :math:`a_X(y)` for a validation data<br>\n","set, we can scan through various values of :math:`\\zeta` on a fine 1D grid and calcuate<br>\n","the accuracy score. Our goal is to pick the :math:`\\zeta` with the<br>\n","largest accuracy score.<br>\n"]},{"cell_type":"code","execution_count":36,"metadata":{},"outputs":[],"source":["@ct.electron\n","def threshold_scan_acc_score(\n","    scores: torch.Tensor, truth_labels: torch.Tensor, zeta_min: float, zeta_max: float, steps: int\n",") -> torch.Tensor:\n","    \"\"\"Given the anomaly scores and truth values,\n","    scan over a range of thresholds = [zeta_min, zeta_max] with a\n","    fixed number of steps, calculating the accuracy score at each point.\n","    \"\"\"\n","    accs = torch.empty(steps)\n","    for i, zeta in enumerate(torch.linspace(zeta_min, zeta_max, steps)):\n","        preds = get_preds_given_threshold(zeta, scores)\n","        accs[i] = get_accuracy_score(preds, truth_labels)\n","    return accs"]},{"cell_type":"code","execution_count":37,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_anomaly_score(\n","    callable_proj: callable,\n","    y: torch.Tensor,\n","    T: torch.Tensor,\n","    alpha_star: torch.Tensor,\n","    mu_star: torch.Tensor,\n","    sigma_star: torch.Tensor,\n","    gamma_length: int,\n","    n_samples: int,\n","    get_time_resolved: bool = False,\n","):\n","    \"\"\"Get the anomaly score for an input time series y. We need to pass the\n","    optimal parameters (arguments with suffix _star). Optionally return the\n","    time-resolved score (the anomaly score contribution at a given t).\n","    \"\"\"\n","    scores = torch.empty(T.size()[0])\n","    for i in range(T.size()[0]):\n","        scores[i] = (\n","            1\n","            - F(\n","                callable_proj,\n","                y[i].unsqueeze(0),\n","                T[i].unsqueeze(0),\n","                alpha_star,\n","                mu_star,\n","                sigma_star,\n","                gamma_length,\n","                n_samples,\n","            )\n","        ).square()\n","    if get_time_resolved:\n","        return scores, scores.mean()\n","    else:\n","        return scores.mean()"]},{"cell_type":"code","execution_count":38,"metadata":{},"outputs":[],"source":["@ct.electron\n","def get_norm_and_anom_scores(\n","    X_norm: torch.Tensor,\n","    X_anom: torch.Tensor,\n","    T: torch.Tensor,\n","    callable_proj: callable,\n","    model_params: dict,\n","    gamma_length: int,\n","    n_samples: int,\n",") -> torch.Tensor:\n","    \"\"\"Get the anomaly scores assigned to input normal and anomalous time series instances.\n","    model_params is a dictionary containing the optimal model parameters.\n","    \"\"\"\n","    alpha = model_params[\"alpha\"]\n","    mu = model_params[\"mu\"]\n","    sigma = model_params[\"sigma\"]\n","    norm_scores = torch.tensor(\n","        [\n","            get_anomaly_score(callable_proj, xt, T, alpha, mu, sigma, gamma_length, n_samples)\n","            for xt in X_norm\n","        ]\n","    )\n","    anom_scores = torch.tensor(\n","        [\n","            get_anomaly_score(callable_proj, xt, T, alpha, mu, sigma, gamma_length, n_samples)\n","            for xt in X_anom\n","        ]\n","    )\n","    return torch.cat([norm_scores, anom_scores])"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now create our second ``@ct.lattice``. We are to test the optimal<br>\n","model against two random models. If our model is trainable, we should<br>\n","see that the trained model is better.<br>\n"]},{"cell_type":"code","execution_count":39,"metadata":{},"outputs":[],"source":["@ct.lattice\n","def threshold_tuning_workflow(\n","    opt_params: dict,\n","    gamma_length: int,\n","    n_samples: int,\n","    probs_func: callable,\n","    zeta_min: float,\n","    zeta_max: float,\n","    steps: int,\n","    p: int,\n","    num_series: int,\n","    noise_amp: float,\n","    spike_amp: float,\n","    max_duration: int,\n","    t_init: float,\n","    t_end: float,\n","    k: int,\n","    U: callable,\n","    W: callable,\n","    D: callable,\n","    n_qubits: int,\n","    random_model_seeds: torch.Tensor,\n","    W_layers: int,\n",") -> tuple:\n","    \"\"\"A workflow for tuning the threshold value zeta, in order to maximize the accuracy score\n","    for a validation data set. Results are tested against random models at their optimal zetas.\n","    \"\"\"\n","    # Generate datasets\n","    X_val_norm, T = generate_normal_time_series_set(p, num_series, noise_amp, t_init, t_end)\n","    X_val_anom, T = generate_anomalous_time_series_set(\n","        p, num_series, noise_amp, spike_amp, max_duration, t_init, t_end\n","    )\n","    truth_labels = get_truth_labels(X_val_norm, X_val_anom)\n","\n","    # Initialize quantum functions\n","    callable_proj = get_callable_projector_func(k, U, W, D, n_qubits, probs_func)\n","    accs_list = []\n","    scores_list = []\n","    # Evaluate optimal model\n","    scores = get_norm_and_anom_scores(\n","        X_val_norm, X_val_anom, T, callable_proj, opt_params, gamma_length, n_samples\n","    )\n","    accs_opt = threshold_scan_acc_score(scores, truth_labels, zeta_min, zeta_max, steps)\n","    accs_list.append(accs_opt)\n","    scores_list.append(scores)\n","\n","    # Evaluate random models\n","    for seed in random_model_seeds:\n","        rand_params = get_initial_parameters(W, W_layers, n_qubits, seed)\n","        scores = get_norm_and_anom_scores(\n","            X_val_norm, X_val_anom, T, callable_proj, rand_params, gamma_length, n_samples\n","        )\n","        accs_list.append(threshold_scan_acc_score(scores, truth_labels, zeta_min, zeta_max, steps))\n","        scores_list.append(scores)\n","    return accs_list, scores_list"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We now set the input parameters.<br>\n"]},{"cell_type":"code","execution_count":40,"metadata":{},"outputs":[],"source":["threshold_tuning_options = {\n","    \"spike_amp\": 0.4,\n","    \"max_duration\": 5,\n","    \"zeta_min\": 0,\n","    \"zeta_max\": 1,\n","    \"steps\": 100000,\n","    \"random_model_seeds\": [0, 1],\n","    \"W_layers\": 2,\n","    \"opt_params\": results_dict[\"opt_params\"],\n","}"]},{"cell_type":"code","execution_count":41,"metadata":{},"outputs":[],"source":["threshold_tuning_options.update(general_options)"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["As before, we dispatch the lattice to the ``Covalent`` server.<br>\n"]},{"cell_type":"code","execution_count":42,"metadata":{},"outputs":[],"source":["val_dispatch_id = ct.dispatch(threshold_tuning_workflow)(**threshold_tuning_options)\n","ct_val_results = ct.get_result(dispatch_id=val_dispatch_id, wait=True)\n","\n","# Check that the workflow executed successfully\n","assert str(ct_val_results.status) == \"COMPLETED\"\n","\n","accs_list, scores_list = ct_val_results.result"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Now, we can plot the results:<br>\n"]},{"cell_type":"code","execution_count":43,"metadata":{},"outputs":[{"data":{"image/png":"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","text/plain":["<Figure size 432x288 with 6 Axes>"]},"metadata":{"needs_background":"light"},"output_type":"display_data"}],"source":["zeta_xlims = [(0, 0.001), (0.25, 0.38), (0.25, 0.38)]\n","titles = [\"Trained model\", \"Random model 1\", \"Random model 2\"]\n","zetas = torch.linspace(\n","    threshold_tuning_options[\"zeta_min\"],\n","    threshold_tuning_options[\"zeta_max\"],\n","    threshold_tuning_options[\"steps\"],\n",")\n","fig, axs = plt.subplots(ncols=3, nrows=2, sharey=\"row\")\n","for i in range(3):\n","    axs[0, i].plot(zetas, accs_list[i])\n","    axs[0, i].set_xlim(zeta_xlims[i])\n","    axs[0, i].set_xlabel(\"Threshold [$\\zeta$]\")\n","    axs[0, i].set_title(titles[i])\n","    axs[1, i].boxplot(\n","        [\n","            scores_list[i][0 : general_options[\"num_series\"]],\n","            scores_list[i][general_options[\"num_series\"] : -1],\n","        ],\n","        labels=[\"Normal\", \"Anomalous\"],\n","    )\n","    axs[1, i].set_yscale(\"log\")\n","    axs[1, i].axhline(\n","        zetas[torch.argmax(accs_list[i])], color=\"k\", linestyle=\":\", label=\"Optimal $\\zeta$\"\n","    )\n","    axs[1, i].legend()\n","axs[0, 0].set_ylabel(\"Accuracy score\")\n","axs[1, 0].set_ylabel(\"Anomaly score [$a_X(y)$]\")\n","fig.tight_layout()"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Parsing the above, we can see that the optimal model achieves high<br>\n","accuracy when the threshold is tuned using the validation data.<br>\n","On the other hand, the random models return mostly random results<br>\n","(sometimes even worse than random guesses), regardless of how we set the<br>\n","threshold.<br>\n"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Testing the model<br>\n","-----------------<br>\n","<br>\n","Now with optimal thresholds for our optimized and random models, we can perform testing.<br>\n","We already have all of the electrons to do this, so we create<br>\n","the ``@ct.lattice``<br>\n"]},{"cell_type":"code","execution_count":44,"metadata":{},"outputs":[],"source":["@ct.lattice\n","def testing_workflow(\n","    opt_params: dict,\n","    gamma_length: int,\n","    n_samples: int,\n","    probs_func: callable,\n","    best_zetas: list,\n","    p: int,\n","    num_series: int,\n","    noise_amp: float,\n","    spike_amp: float,\n","    max_duration: int,\n","    t_init: float,\n","    t_end: float,\n","    k: int,\n","    U: callable,\n","    W: callable,\n","    D: callable,\n","    n_qubits: int,\n","    random_model_seeds: torch.Tensor,\n","    W_layers: int,\n",") -> list:\n","    \"\"\"A workflow for calculating anomaly scores for a set of testing time series\n","    given an optimal model and set of random models. We use the optimal zetas found in threshold tuning.\n","    \"\"\"\n","    # Generate time series\n","    X_val_norm, T = generate_normal_time_series_set(p, num_series, noise_amp, t_init, t_end)\n","    X_val_anom, T = generate_anomalous_time_series_set(\n","        p, num_series, noise_amp, spike_amp, max_duration, t_init, t_end\n","    )\n","    truth_labels = get_truth_labels(X_val_norm, X_val_anom)\n","\n","    # Prepare quantum functions\n","    callable_proj = get_callable_projector_func(k, U, W, D, n_qubits, probs_func)\n","    accs_list = []\n","    # Evaluate optimal model\n","    scores = get_norm_and_anom_scores(\n","        X_val_norm, X_val_anom, T, callable_proj, opt_params, gamma_length, n_samples\n","    )\n","    preds = get_preds_given_threshold(best_zetas[0], scores)\n","    accs_list.append(get_accuracy_score(preds, truth_labels))\n","    # Evaluate random models\n","    for zeta, seed in zip(best_zetas[1:], random_model_seeds):\n","        rand_params = get_initial_parameters(W, W_layers, n_qubits, seed)\n","        scores = get_norm_and_anom_scores(\n","            X_val_norm, X_val_anom, T, callable_proj, rand_params, gamma_length, n_samples\n","        )\n","        preds = get_preds_given_threshold(zeta, scores)\n","        accs_list.append(get_accuracy_score(preds, truth_labels))\n","    return accs_list"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["We dispatch it to the Covalent server with the appropriate parameters.<br>\n"]},{"cell_type":"code","execution_count":45,"metadata":{},"outputs":[],"source":["testing_options = {\n","    \"spike_amp\": 0.4,\n","    \"max_duration\": 5,\n","    \"best_zetas\": [zetas[torch.argmax(accs)] for accs in accs_list],\n","    \"random_model_seeds\": [0, 1],\n","    \"W_layers\": 2,\n","    \"opt_params\": results_dict[\"opt_params\"],\n","}"]},{"cell_type":"code","execution_count":46,"metadata":{},"outputs":[],"source":["testing_options.update(general_options)"]},{"cell_type":"code","execution_count":47,"metadata":{},"outputs":[],"source":["test_dispatch_id = ct.dispatch(testing_workflow)(**testing_options)\n","ct_test_results = ct.get_result(dispatch_id=test_dispatch_id, wait=True)\n","\n","# Check that the workflow executed successfully\n","assert str(ct_val_results.status) == \"COMPLETED\"\n","\n","accs_list = ct_test_results.result"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["Finally, we plot the results below.<br>\n"]},{"cell_type":"code","execution_count":48,"metadata":{},"outputs":[{"data":{"image/png":"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O48333yTs846i8suu6xi2LJly3jllVe47bbb6NOnD1dccQUzZ85k+vTpFVcq+S655BImTZrEjBkz+OSTT3jyyScBOOuss7j44ouZNm0aEyZMYI899uCZZ57hiSeeYOLEiUybNm2TpFWd119/nWHDhjF+/HhatGjBY489xuuvv87YsWO58soriQhmzpzJr3/9a8aMGcO0adO4/fbbadWqFblcjqeeSmo2Bg8ezMknn1yn+wWq40RgZkX1ySef0LVrV3bffXcWLVrEcccdByRJ5tRTT6Vjx44VX87levbsSevWrWnRogUHHXQQ77zzDhMnTiSXy9G2bVu22247TjvttIrxX3nlFc4880wAzjnnHF566aWKYSeeeCKS6NSpE7vtthudOnWiSZMmHHzwwcyfP/8z8Y4dO5bDDz+cTp06MWbMGGbOnMnKlStZuHAh3/ve94Dkpq2WLVsyevRo+vbtS8uWLQHYZZddPjO/yo477riK8SKCa6+9ls6dO9OrVy8WLlzIokWLGDNmDKeeeipt2rTZZL4//OEPeeCBB4Dk6qhv3y1z+9Xno4LKzBrMuHHjKrqbN2++Sblly5ablFu3br1JuU2bNpuUd99991qXV95GUFZWxvHHH89dd93FZZddxi9/+Uu++c1v8thjjzF//nxyuVzFNNtvv31Fd9OmTVm3bl1dVnET5fNq0qTJJvNt0qTJZ+a7evVqLrroIiZPnkz79u258cYb63VHdrNmzdiwYUPFPPPtuOOOFd1///vfWbx4MVOmTKF58+Z06NChxuUdddRRzJ8/n3HjxrF+/Xo6duxY59iq4isCM2sQLVu25I477uD3v/8969atY8WKFey1V/LA4fKqqpocfvjhjB8/nqVLl7J27VqGDh1aMezII49k8ODBQPLlevTRR9crxvIv4TZt2rBq1SqGDRsGQKtWrWjXrh2PP/44AGvWrKGsrIzjjjuOBx54gLKyMgA+/PBDIHlc/pQpUwAq5lGVFStWsOuuu9K8eXPGjh3LO+8kT4o+9thjGTp0KEuXLt1kvgDnnnsuZ5555ha7GgAnAjNrQN26daNz58488sgjXH311VxzzTV069atoDP+PfbYgxtvvJEePXpw1FFHceCBB1YMu/POO3nggQfo3LkzDz30ELfffnu94tt555258MIL6dixI8cffzyHHnpoxbCHHnqIO+64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","text/plain":["<Figure size 432x288 with 1 Axes>"]},"metadata":{"needs_background":"light"},"output_type":"display_data"}],"source":["plt.figure()\n","plt.bar([1, 2, 3], accs_list)\n","plt.axhline(0.5, color=\"k\", linestyle=\":\", label=\"Random accuracy\")\n","plt.xticks([1, 2, 3], [\"Trained model\", \"Random model 1\", \"Random model 2\"])\n","plt.ylabel(\"Accuracy score\")\n","plt.title(\"Accuracy scores for trained and random models\")\n","leg = plt.legend()"]},{"attachments":{},"cell_type":"markdown","metadata":{},"source":["As can be seen, once more, the trained model is far more accurate than<br>\n","the random models. Awesome! Now that we're done with the calculations in<br>\n","this tutorial, we just need to remember to shut down the Covalent server<br>\n"]}],"metadata":{"kernelspec":{"display_name":"pennylane-nb","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.8.13"},"vscode":{"interpreter":{"hash":"fd869327155fe353a03632081ba966768ca3fb2acd5f6c90184782c22042853d"}}},"nbformat":4,"nbformat_minor":2}
